//
//  HJAWSS3Manager.m
//  KaiChat
//
//  Created by lx on 2024/4/16.
//

#import "HJAWSS3Manager.h"
#import <AWSS3/AWSS3.h>
#import <AWSCore/AWSCore.h>

@interface AWSAuthConfiguration: NSObject

@property (nonatomic, strong) NSString *s3key;
@property (nonatomic, strong) NSString *endpoint;
@property (nonatomic, strong) NSString *accessKey;
@property (nonatomic, strong) NSString *secretKey;
@property (nonatomic, strong) NSString *bucket;
@property (nonatomic, strong) NSString *baseUrl;
@property (nonatomic, assign) AWSRegionType regionType;

@end

@implementation AWSAuthConfiguration


@end
 

@interface HJAWSS3Manager()

@property (nonatomic, strong) AWSAuthConfiguration *configuration;
@property (nonatomic, strong) AWSS3 *S3;


@end


@implementation HJAWSS3Manager


static HJAWSS3Manager *_S3Manager = nil;

+ (HJAWSS3Manager *)shareS3Manager {
    return [[HJAWSS3Manager alloc] init];
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (_S3Manager == nil) {
            _S3Manager = [super allocWithZone:zone];
        }
    });
    return _S3Manager;
}

- (AWSAuthConfiguration *)configuration {
    if (!_configuration) {
        _configuration = [[AWSAuthConfiguration alloc] init];
        _configuration.s3key = @"AWSS3Key";
        
        if (getAppEnvType() == HJAppEnvTypePro) {
            _configuration.endpoint = @"https://s3.ap-southeast-1.amazonaws.com";
            _configuration.secretKey = @"/K0PzKtZOqpmJ4sRxCXFQizyIF2X3l1QiPjw+ceF";
            _configuration.accessKey = @"AKIAQTXAU7ZM3SQ4HFIY";
            _configuration.bucket = @"img-fs";
        } else {
            _configuration.endpoint = @"https://s3.ap-southeast-1.amazonaws.com";
            _configuration.secretKey = @"4CuaQg4zdCjhFSB7ggEYKj/GnjmCoa8qIps7flwl";
            _configuration.accessKey = @"AKIAQTXAU7ZMYGKKH5G7";
            _configuration.bucket = @"img-fs-dev";
        }

        _configuration.regionType = AWSRegionAPSoutheast1;
        NSURL *URL = [NSURL URLWithString:_configuration.endpoint];
        _configuration.baseUrl = [NSString stringWithFormat:@"%@://%@.%@", URL.scheme, _configuration.bucket, URL.host];
    }
    return _configuration;
}

- (instancetype)init {
    if (self = [super init]) {
        AWSEndpoint *endpoint = [[AWSEndpoint alloc] initWithRegion:AWSRegionAPSoutheast1
        service:AWSServiceS3 URL:[NSURL URLWithString:self.configuration.endpoint]];
         AWSStaticCredentialsProvider *credentialsProvider = [[AWSStaticCredentialsProvider alloc] initWithAccessKey:self.configuration.accessKey secretKey:self.configuration.secretKey];
        AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc] initWithRegion:self.configuration.regionType endpoint:endpoint credentialsProvider:credentialsProvider];
        [AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;
        [AWSS3 registerS3WithConfiguration:configuration forKey:self.configuration.s3key];
        _S3 = [AWSS3 S3ForKey:self.configuration.s3key];
    }
    return self;
}

- (void)downloadWithObjectKey:(NSString *)objectKey toLocalPath:(NSString *)localPath progressBlock:(void(^)(int64_t totalBytesWritten, int64_t bytesWritten))progressBlock completionHandler:(void (^)(NSError * _Nullable error))completionHandler {
  
    AWSS3GetObjectRequest *request = [[AWSS3GetObjectRequest alloc] init];
    request.key = objectKey;
    request.bucket = self.configuration.bucket;
    request.downloadingFileURL = [NSURL fileURLWithPath:localPath];
    [request setDownloadProgress:^(int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {
        !progressBlock ?: progressBlock(totalBytesExpectedToWrite, totalBytesWritten);
    }];
    [self.S3 getObject:request completionHandler:^(AWSS3GetObjectOutput * _Nullable response, NSError * _Nullable error) {
        NSLog(@"downloadFileWithObjectKey-- response = %@, path = %@, error = %@", response, localPath, error);
        !completionHandler ?: completionHandler(error);
    }];
}


- (void)updateWithData:(NSData *)data objectKey:(NSString *)objectKey progressBlock:(void(^)(int64_t totalBytesSent, int64_t completeBytes))progressBlock completionHandler:(void (^)(NSString * _Nullable url, NSError * _Nullable error))completionHandler {
    
    if (data.length == 0) {
        dispatch_async_main_queue(^{
            !completionHandler ?: completionHandler([NSString stringWithFormat:@"%@/%@", self.configuration.baseUrl, objectKey], [NSError errorWithCode:-10001 errorReason:@"fileData is nil"]);
        });
        return;
    }
    
    NSLog(@"updateWithData = %ld", data.length);

    if (data.length > 20 * 1024 * 1024) {
        NSLog(@"分片上传");
        [self chunkedUploadWithData:data objectKey:objectKey progressBlock:progressBlock completionHandler:completionHandler];
        return;
    }
    
 
    NSLog(@"直接上传 objectKey = %@", objectKey);
    AWSS3PutObjectRequest *request = [[AWSS3PutObjectRequest alloc] init];
    request.key = objectKey;
    request.body = data;
    request.bucket = self.configuration.bucket;
    request.contentLength = @(data.length);
    [request setUploadProgress:^(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) {
        NSLog(@"totalBytesSent %lld, totalBytesExpectedToSend = %lld", totalBytesSent, totalBytesExpectedToSend);
        
        !progressBlock ?: progressBlock(data.length, totalBytesSent);
    }];
    [self.S3 putObject:request completionHandler:^(AWSS3PutObjectOutput * _Nullable response, NSError * _Nullable error) {
        NSLog(@"error = %@, response = %@", error, response);
        dispatch_async_main_queue(^{
            !completionHandler ?: completionHandler([NSString stringWithFormat:@"%@/%@", self.configuration.baseUrl, objectKey], error);
        });
    }];

}


// AWSUploadConfig: 自定义对象
- (void)chunkedUploadWithData:(NSData *)data objectKey:(NSString *)objectKey progressBlock:(void(^)(int64_t totalBytesSent, int64_t completeBytes))progressBlock completionHandler:(void (^)(NSString * _Nullable url, NSError *  _Nullable error))completionHandler {
 
    // 1.初始化分片上传事件: 调用[[AWSS3CreateMultipartUploadRequest alloc] init]方法返回OSS创建的全局唯一的uploadId
    AWSS3CreateMultipartUploadRequest *createRequest = [[AWSS3CreateMultipartUploadRequest alloc] init];
    createRequest.bucket = self.configuration.bucket;
    createRequest.key = objectKey;
    
    TJWeakify(self)
    
    [self.S3 createMultipartUpload:createRequest completionHandler:^(AWSS3CreateMultipartUploadOutput * _Nullable response, NSError * _Nullable error) {
        if (error) {
            NSLog(@"createMultipartUpload 创建失败: %@", error);
            !completionHandler ?: completionHandler(nil, error);
        } else {
            NSLog(@"createMultipartUpload 创建成功: %@", response);
            [weakself uploadPartData:data uploadOutput:response progressBlock:^(int64_t totalBytesSent, int64_t completeBytes) {
                !progressBlock ?: progressBlock(totalBytesSent, completeBytes);
            } completionHandler:^(NSArray *parts, NSError *error) {
                if (parts.count > 0) {
                    [weakself multipartUploadParts:parts uploadOutput:response completionHandler:completionHandler];
                } else {
                    !completionHandler ?: completionHandler(nil, error);
                }
            }];
        }
    }];
}

/// 2.上传分片: 调用uploadPart方法上传分片数据
- (void)uploadPartData:(NSData *)data uploadOutput:(AWSS3CreateMultipartUploadOutput *)uploadOutput progressBlock:(void(^)(int64_t totalBytesSent, int64_t completeBytes))progressBlock completionHandler:(void(^)(NSArray *parts, NSError *error))completionHandler {
    
    // 默认每片5M
    NSInteger partSize = 20 * 1024 * 1024;
    NSUInteger chunkSize = ceil((float)data.length /(unsigned long) partSize);
    
    __block NSInteger location = 0;
    __block NSMutableArray<AWSS3CompletedPart *> *parts = [NSMutableArray array];
    
    int64_t totalBytes = data.length;
    __block int64_t completeBytes = 0;
    
    for (int i = 1; location < data.length; i++) {
        partSize = MIN(partSize, data.length - location);
        NSData *partData = [data subdataWithRange:NSMakeRange(location, partSize)];
        location += partSize;

        AWSS3UploadPartRequest *uploadRequest = [[AWSS3UploadPartRequest alloc] init];
        uploadRequest.bucket = uploadOutput.bucket;
        uploadRequest.key = uploadOutput.key;
        uploadRequest.uploadId = uploadOutput.uploadId;
        uploadRequest.partNumber = @(i);
        uploadRequest.body = partData;
        uploadRequest.contentLength = @(partData.length);
        uploadRequest.uploadProgress = ^(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend) {
            completeBytes += bytesSent;
            !progressBlock ?: progressBlock(totalBytes, MIN(completeBytes, totalBytes));
        };
        
        [self.S3 uploadPart:uploadRequest completionHandler:^(AWSS3UploadPartOutput * _Nullable response, NSError * _Nullable error) {
            if (error) {
                NSLog(@"uploadPart 失败: %@", error);
                !completionHandler ?: completionHandler(nil, error);
            } else {
                
                NSLog(@"上传分片完成response = %@", response);
                
                AWSS3CompletedPart *part = [[AWSS3CompletedPart alloc] init];
                part.ETag = response.ETag;
                part.partNumber = @(i);
                [parts addObject:part];
                
                if (parts.count == chunkSize) {
                    // 分片块 要求升序排列
                    [parts sortUsingComparator:^NSComparisonResult(AWSS3CompletedPart *obj1, AWSS3CompletedPart *obj2) {
                        if ([obj1.partNumber integerValue] < [obj2.partNumber integerValue]) {
                            return NSOrderedAscending;
                        } else {
                            return NSOrderedDescending;
                        }
                    }];
                    !completionHandler ?: completionHandler(parts, nil);
                }
            }
        }];
        
    }
}

/// 3.完成分片上传: 所有分片上传完成后，调用completeMultipartUpload方法将所有分片合并成完整的文件
- (void)multipartUploadParts:(NSArray *)parts  uploadOutput:(AWSS3CreateMultipartUploadOutput *)uploadOutput completionHandler:(void (^)(NSString * _Nullable url, NSError *  _Nullable error))completionHandler {
    
    AWSS3CompletedMultipartUpload *multipartUpload = [[AWSS3CompletedMultipartUpload alloc] init];
    multipartUpload.parts = parts;
    
    AWSS3CompleteMultipartUploadRequest *completeRequest = [[AWSS3CompleteMultipartUploadRequest alloc] init];
    completeRequest.bucket = uploadOutput.bucket;
    completeRequest.key = uploadOutput.key;
    completeRequest.uploadId = uploadOutput.uploadId;
    completeRequest.multipartUpload = multipartUpload;
    
    [self.S3 completeMultipartUpload:completeRequest completionHandler:^(AWSS3CompleteMultipartUploadOutput * _Nullable response, NSError * _Nullable error) {
        if (error) {
            NSLog(@"completeMultipartUpload 失败: %@", error);
        } else {
            // 移除AWSS3
            NSLog(@"completeMultipartUpload 成功: %@", response);
        }
        
        dispatch_async_main_queue(^{
            !completionHandler ?: completionHandler([NSString stringWithFormat:@"%@/%@", self.configuration.baseUrl, uploadOutput.key], error);
        });
    }];
}

// 4.列举正在上传的分片
- (void)abortMultipartUploadCompletionHandler:(void (^)(NSError *  _Nullable error))completionHandler {
    
    AWSS3ListMultipartUploadsRequest *listRequest = [[AWSS3ListMultipartUploadsRequest alloc] init];
    listRequest.bucket = self.configuration.bucket;

    [self.S3 listMultipartUploads:listRequest completionHandler:^(AWSS3ListMultipartUploadsOutput * _Nullable response, NSError * _Nullable error) {
        if (error) {
            NSLog(@"listMultipartUploads 上传失败: %@", error);
        } else {
            NSInteger count = response.uploads.count;
            
            NSLog(@"listMultipartUploads: %ld", count);
            
            /// 5. 取消分片上传
            for (AWSS3MultipartUpload *upload in response.uploads) {
                
                AWSS3AbortMultipartUploadRequest *abortRequest = [[AWSS3AbortMultipartUploadRequest alloc] init];
                abortRequest.bucket = response.bucket;
                abortRequest.key = upload.key;
                abortRequest.uploadId = upload.uploadId;
                
                [self.S3 abortMultipartUpload:abortRequest completionHandler:^(AWSS3AbortMultipartUploadOutput * _Nullable response, NSError * _Nullable error) {
                    if (error) {
                        NSLog(@"abortMultipartUpload 失败: %@", error);
                    } else {
                        NSLog(@"abortMultipartUpload 成功");
                    }
                }];
            }
        }
    }];
}




@end
